#include "dispatcher.h"

namespace cs
{
	dispatcher::dispatcher(registry::session_ptr session):
		session_(session), logger("cs_dispatcher")
	{
		generate_dispatching_map();
		session_addr = service_registry::instance().add_session(session_);
		send_session_address();
	}

	void dispatcher::generate_dispatching_map()
	{
		dispatching_map = boost::assign::map_list_of
			(REGISTER_SERVICE_REQUEST,
			boost::bind(&dispatcher::handle<register_service_req_proto>, this, _1))
			(SUBSCRIBE_SERVICE_REQUEST,
			boost::bind(&dispatcher::handle<subscribe_service_req_proto>, this, _1));
	}
	
	void dispatcher::send_session_address()
	{
		const std::string msg = prepare_service_addr_proto(session_addr, "my_addr").serialize();
		session_->deliver(message(SERVICE_ADDRESS, 0, session_addr, msg));
	}

	void dispatcher::dispatch(message_handle msg)
	{
		dispatching_map_t::iterator it = dispatching_map.find(msg->id());
		if ( it != dispatching_map.end()) {
			it->second(msg);
		} else {
			forward(msg);
		}
	}

	void dispatcher::forward(message_handle msg)
	{
		registry::session_ptr session = service_registry::instance().find_session(msg->receiver());
		if (session) {
			session->deliver(*msg.get());
		}
	}

	void dispatcher::handle(register_service_req_proto pmsg)
	{
		register_service_resp_proto resp;

		if (service_registry::instance().register_service(session_, pmsg->service_name())) {
			resp->set_status(RegisterServiceResponse::SUCCESS);
			notif_subscribers(pmsg->service_name());
		} else {
			resp->set_status(RegisterServiceResponse::REJECTED);
		}
		session_->deliver(message(REGISTER_SERVICE_RESPONSE, 0, session_addr, resp.serialize()));
	}

	void dispatcher::handle(subscribe_service_req_proto pmsg)
	{
		address addr = service_registry::instance().subscribe_sevice(session_, pmsg->service_name());
		if ( addr != 0 ) {
			const std::string msg = prepare_service_addr_proto(addr, pmsg->service_name()).serialize();
			session_->deliver(message(SERVICE_ADDRESS, 0, session_addr, msg));
		}
	}

	void dispatcher::notif_subscribers(const std::string& service_name)
	{
		registry::subscriber_list_t list = service_registry::instance().get_subscribers(service_name);
		registry::subscriber_list_t::const_iterator it = list.begin();
		
		const std::string msg = prepare_service_addr_proto(session_addr, service_name).serialize();
		
		for(; it != list.end(); it++) {
			address addr = service_registry::instance().find_address(*it);
			(*it)->deliver(message(SERVICE_ADDRESS, 0, addr, msg));
		}
	}

	service_address_proto dispatcher::prepare_service_addr_proto(const address& addr,const std::string& name)
	{
		service_address_proto proto;
		proto->set_address(addr);
		proto->set_service_name(name);

		return proto;
	}
}